/** @file    ObjectAllocator.cpp
 *  @author  Alessandro Polo
 *  @version $Id: ObjectAllocator.cpp 2831 2010-08-02 22:15:14Z alex $
 *  @brief
 * File containing methods for the wosh::ObjectAllocator class.
 * The header for this class can be found in ObjectAllocator.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/ObjectAllocator.h>
 #include <core/ObjectFactory.h>
 #include <core/WoshKernel.h>
 #include <core/SystemError.h>
 #include <core/MethodsCommon.h>
 #include <core/Table.h>
 #include <core/Tree.h>
 #include <core/List.h>
 #include <core/DataModels.h>
 #include <core/Functional.h>
 #include <core/Utilities.h>
 #include <stdio.h> // stderr, FILE


using namespace std;
namespace wosh {

// to be called in ObjectTypeAdapter implementation
void ObjectTypeAdapter::registerMySelf() const {
	StaticAllocator::getInstance().registerType(this);
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

ObjectAllocator::ObjectAllocator()
	: Object(NULL, "wosh.ObjectAllocator") {
	Log.setContext( "wosh.ObjectAllocator", true );
	Log.setIndent( 1 );
	Log.setLevel( LOG_INFO );
 }

ObjectAllocator::~ObjectAllocator() {
	this->typesInfo.clear();
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int ObjectAllocator::validateNode_( const ObjectTypeAdapter* currNode, double version_pattern, bool allocable_only )const {
	if ( currNode == NULL ) return -1;
	if ( version_pattern < 0.0 ) { // precise pattern match
		if ( currNode->version != -version_pattern )
			return 101;
	 }
	else if ( version_pattern != 0.0 ) {
		if ( currNode->version < version_pattern )
			return 102;
	 }
	if ( allocable_only ) {
		if ( !currNode->allocable )
			return 1;
	 }
	return 0;
 }

ObjectTypeNode* ObjectAllocator::findChildOf_( ObjectTypeNode* currentNode, double version_pattern, bool allocable_only ) {
	if ( currentNode == NULL ) return NULL;

	int ret = validateNode_(currentNode->info, version_pattern, allocable_only);
	if ( ret == 0 )
		return currentNode;

	ObjectTypeNode* found = NULL;
	ObjectTypeNode* child_container = NULL;
	std::vector<std::string>::const_iterator it;
	for( it=currentNode->children.begin(); it!=currentNode->children.end(); ++it ) {
		child_container = this->typesInfo.find(*it);
		if ( child_container == NULL ) continue; // should never happen
		found = findChildOf_(child_container, version_pattern, allocable_only );
		if ( found != NULL )
			return found;
	 }
	return NULL;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool ObjectAllocator::isSupported( const std::string& classname, double version_pattern, bool allocable_only ) const {
	this->typesInfo.transactionBeginRead();
	const ObjectTypeNode* container = this->typesInfo.find(classname);
	if ( container == NULL ) {
		this->typesInfo.transactionEnd();
		return false;
	 }
	int ret = validateNode_(container->info, version_pattern, allocable_only);
	this->typesInfo.transactionEnd();
	return (ret == 0);
 }

WRESULT ObjectAllocator::gatherInfoOf( std::vector<ObjectTypeInfo>& parent_list, const std::string& classname, double version_pattern ) const {
	this->typesInfo.transactionBeginRead();
	const ObjectTypeNode* container = this->typesInfo.find(classname);
	if ( container == NULL ) {
		this->typesInfo.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	int ret = validateNode_(container->info, version_pattern, false);
	if ( ret !=  0 ) {
		this->typesInfo.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	// first element is the type itself
	ObjectTypeInfo type_info(*container->info);
	parent_list.push_back(type_info);
	// add parent
	if ( !container->info->parent.empty() ) {
		const ObjectTypeNode* parent_container = this->typesInfo.find(container->info->parent);
		if ( parent_container != NULL && parent_container->info != NULL ) {
			ObjectTypeInfo parent_info(*parent_container->info);
			parent_list.push_back(parent_info);
		 }
		else {
			ObjectTypeInfo parent_info;
			parent_info.name = container->info->parent;
			parent_info.version = -1.0;
			parent_list.push_back(parent_info);
		 }
	}
	// search if class is CHILD of another interface..
	// there is one direct parent, but because of multiple inheritance
	// there may be more reference inside 'children' fields..
	MapT<std::string, ObjectTypeNode*>::ConstIterator it;
	const MapT<std::string, ObjectTypeNode*>::ConstIterator it_end = this->typesInfo.end();
	for( it=this->typesInfo.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL || it->second->info == NULL ) continue;
		if ( it->first == container->info->parent ) continue;
		if ( !it->second->isChild(classname) ) continue;
		// found another parent!
		ObjectTypeInfo parent_info(*it->second->info);
		parent_list.push_back(parent_info);	
	 }

	///@todo parents of parent(s)

	this->typesInfo.transactionEnd();
	return WRET_OK;
 }

int ObjectAllocator::addChildsOf_( std::vector<ObjectTypeInfo>& class_list, const ObjectTypeNode* node, bool allocable ) const {
	if ( node == NULL ) return 0;
	if ( !allocable || node->info->allocable )
		class_list.push_back(*node->info);
	int count = 0;
	// children are candidates
	std::vector<std::string>::const_iterator it;
	for( it=node->children.begin(); it!=node->children.end(); ++it ) {
		const ObjectTypeNode* child = this->typesInfo.find(*it);
		if ( child == NULL || child->info == NULL ) continue;
		// found a child?
		count += addChildsOf_(class_list , child, allocable);
		///@todo child of child(s)
	 }
	return count;
 }

WRESULT ObjectAllocator::findChildsOf( std::vector<ObjectTypeInfo>& class_list, const std::string& classname, double version_pattern, bool allocable ) const {
	this->typesInfo.transactionBeginRead();
	const ObjectTypeNode* container = this->typesInfo.find(classname);
	if ( container == NULL ) {
		this->typesInfo.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	int ret = validateNode_(container->info, version_pattern, false);
	if ( ret !=  0 ) {
		this->typesInfo.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	addChildsOf_(class_list, container, allocable);
	this->typesInfo.transactionEnd();
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

IReflection* ObjectAllocator::createTypeOf( const std::string& classname, double version_pattern, bool strict  ) {
	this->typesInfo.transactionBeginRead();
	ObjectTypeNode* container = this->typesInfo.find(classname);
	if ( container == NULL ) {
		// definetly not found
		this->typesInfo.transactionEnd();
		return NULL;
	 }
	ObjectTypeNode* found = NULL;
	int ret = validateNode_(container->info, version_pattern, true);
	if ( ret == 0 )
		found = container;
	else if ( !strict )
		found = findChildOf_( container, version_pattern, true );

	if ( found == NULL || found->info == NULL ) {
		// definetly not found
		this->typesInfo.transactionEnd();
		return NULL;
	 }
	IReflection* object = found->info->create();
	this->typesInfo.transactionEnd();
	if ( object != NULL ) {
		this->allocatedObjects.transactionBeginWrite();
		this->allocatedObjects.push_back(object);
		this->allocatedObjects.transactionEnd();
	 }
	return object;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ObjectAllocator::destroy( IReflection* object ) {
	if ( object == NULL ) return WRET_ERR_PARAM;
	Log(LOG_VERBOSE, ":destroy(%p) : Unregistering Object.. [%s]", object, object->getClassName() );
	this->allocatedObjects.transactionBeginWrite();
	this->allocatedObjects.pop(object);
	this->allocatedObjects.transactionEnd();
	Log(LOG_INFO, ":destroy(%p) : (inline) Destroying.. [%s]", object, object->getClassName() );
	delete object;
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ObjectAllocator::registerType( const ObjectTypeAdapter* info ) {
	// validate
	if ( info == NULL ) return WRET_ERR_PARAM;
	if ( info->name.empty() ) return WRET_ERR_PARAM;

	this->typesInfo.transactionBeginWrite();
	ObjectTypeNode* container = this->typesInfo.find(info->name);

	if ( container != NULL ) {
		// updating an type!?
		if ( container->info == NULL ) { // probably it was a dummy parent!
			Log(LOG_VERBOSE, ":registerType(%p) : Overwriting previously null type %s", info, info->name.c_str() );
			container->info = info->clone();
		 }
		else {
			Log(LOG_WARNING, ":registerType(%p) : Overwriting type %s", info, info->name.c_str() );
			if ( !container->info->parent.empty() ) {
				ObjectTypeNode* parent_container = this->typesInfo.find(container->info->parent);
				if ( parent_container != NULL ) {
					parent_container->delChild(info->name);
				 }
			 }
		 }
 	 }
	else { // registering a new type
		Log(LOG_INFO, ":registerType(%p) : Registering new type %s [%g]", info, info->name.c_str(), info->version );
		container = new ObjectTypeNode(info->clone());
		this->typesInfo.set( info->name, container );
	 }
	if ( container->info->name == container->info->parent )
		container->info->parent = "";

	// now fix parent reference: (add itself as child of parent interface)
	if ( !info->parent.empty() && info->parent != info->name ) {
		Log(LOG_VERBOSE, ":registerType(%p) : Fixing parent type %s", info, info->parent.c_str() );
		ObjectTypeNode* parent_container = this->typesInfo.find(info->parent);
		if ( parent_container == NULL ) { // inline registration as dummy
			parent_container = new ObjectTypeNode(NULL);
			this->typesInfo.set( info->parent, parent_container );
		 }
		// add this as child if missing
		parent_container->setChild(info->name);
	 }

	// now fix children references: (register in children-list who claim to be a child)
	MapT<std::string, ObjectTypeNode*>::Iterator it;
	const MapT<std::string, ObjectTypeNode*>::ConstIterator it_end = this->typesInfo.end();
	for( it=this->typesInfo.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL || it->second->info == NULL ) continue;
		if ( it->second->info->parent != info->name ) continue;
		container->setChild(it->second->info->name);
	 }

	this->typesInfo.transactionEnd();
	return WRET_OK;
 }

 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ObjectAllocator::unRegisterType( const char* classname, bool remove_children ) {
	if ( classname == NULL ) return WRET_ERR_PARAM;
	this->typesInfo.transactionBeginWrite();
	ObjectTypeNode* container = this->typesInfo.find(classname);
	if ( container == NULL ) {
		Log(LOG_CRITICAL, ":unRegisterType(%s) FAILED#1 Type not found!", classname );
		this->typesInfo.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	if ( container->children.empty() ) {
		this->typesInfo.remove(classname, true);
		Log(LOG_INFO, ":unRegisterType(%s) Type removed", classname );
		this->typesInfo.transactionEnd();
		return WRET_OK;
	 }
	if ( !remove_children ) {
		Log(LOG_WARNING, ":unRegisterType(%s) Type removed", classname );
		this->typesInfo.transactionEnd();
		return WRET_ERR_ILLEGAL_USE;
	 }
	///@bug not recursive!
	std::vector<std::string>::const_iterator it;
	for( it=container->children.begin(); it!=container->children.end(); ++it ) {
		if ( *it == classname ) continue;
		this->typesInfo.remove(*it, true);
	 }
	this->typesInfo.remove(classname, true);
	this->typesInfo.transactionEnd();
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ObjectAllocator::mergeTree_( Tree& obj_info_tree, bool children_tree ) const {
	WRESULT ret = WRET_OK;
	MapT<std::string, ObjectTypeNode*>::ConstIterator it;
	const MapT<std::string, ObjectTypeNode*>::ConstIterator it_end = this->typesInfo.end();
	for( it=this->typesInfo.begin(); it!=it_end; ++it ) {
		if ( children_tree )
			mergeTreeNodeChild_(it->first, obj_info_tree.getRoot());
		else
			mergeTreeNodeParent_(it->first, obj_info_tree.getRoot());
	 }
	return ret;
 }

void ObjectAllocator::mergeTreeNodeChild_( const string& classname, TreeNode* parentNode ) const {
	if ( classname.empty() ) return;
	const ObjectTypeNode* objNode = this->typesInfo.find(classname);
	TreeNode* cNode = NULL;
	if ( objNode == NULL || objNode->info == NULL ) {
		ObjectTypeInfo* objTI = new ObjectTypeInfo(classname.c_str());
		cNode = new TreeNode(parentNode, objTI);
		if ( objNode == NULL ) return;
	 }
	else
		cNode = new TreeNode(parentNode, new ObjectTypeInfo(*objNode->info) );
	// recursive
	std::vector<std::string>::const_iterator it;
	for( it=objNode->children.begin(); it!=objNode->children.end(); ++it ) {
		mergeTreeNodeChild_( *it, cNode );
	 }
 }

void ObjectAllocator::mergeTreeNodeParent_( const std::string& classname, TreeNode* parentNode ) const {
	if ( classname.empty() ) return;
	const ObjectTypeNode* objNode = this->typesInfo.find(classname);
	TreeNode* cNode = NULL;
	if ( objNode == NULL || objNode->info == NULL ) {
		ObjectTypeInfo* objTI = new ObjectTypeInfo(classname.c_str());
		cNode = new TreeNode(parentNode, objTI);
		return;
	 }
	else
		cNode = new TreeNode(parentNode, new ObjectTypeInfo(*objNode->info) );
	// recursive
	if ( !objNode->info->parent.empty() )
		mergeTreeNodeParent_(objNode->info->parent, cNode);
	// find other parents
	MapT<std::string, ObjectTypeNode*>::ConstIterator it;
	const MapT<std::string, ObjectTypeNode*>::ConstIterator it_end = this->typesInfo.end();
	for( it=this->typesInfo.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->first == classname ) continue;
		if ( it->first == objNode->info->parent ) continue;
		if ( it->second->isChild(classname) )
			mergeTreeNodeParent_(it->first, cNode);
	 }
	///@bug multiple inheritance hot handled
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<ObjectAllocator>* ObjectAllocator::getDataFields() {
	DataFieldCollector<ObjectAllocator>* fields = new DataFieldCollector<ObjectAllocator>();
	fields->add<const std::string&, Variant>(&ObjectAllocator::getName, "Name", 0);
	fields->add<const char*, Variant>(&ObjectAllocator::getClassName, "Type", 1);
	DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tIReflectionList&, int, Variant>* dfwAllocatedCountInt;
	dfwAllocatedCountInt = new DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tIReflectionList&, int, Variant>(&ObjectAllocator::getAllocatedObjects, &ObjectAllocator::tIReflectionList::count, "#Allocated", 2);
	fields->add(dfwAllocatedCountInt);
	DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tStrObjectTypeNodeMap&, int, Variant>* dfwTypesCountInt;
	dfwTypesCountInt = new DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tStrObjectTypeNodeMap&, int, Variant>(&ObjectAllocator::getObjectsInfo, &ObjectAllocator::tStrObjectTypeNodeMap::count, "#Types", 3);
	fields->add(dfwTypesCountInt);
	fields->add<const std::string&,Variant>(&ObjectAllocator::getEntityID, "EntityID", 4, false);
	fields->add<const Permission&,Permission>(&ObjectAllocator::getPermission, "Permission", 5, false);
	return fields;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ObjectAllocator::dump() {
	FILE* outfile = stderr;
	outfile = fopen("ObjectFactory-dump.log", "w");

	MapT<std::string, ObjectTypeNode*>::ConstIterator it;
	const MapT<std::string, ObjectTypeNode*>::ConstIterator it_end = this->typesInfo.end();
	for( it=this->typesInfo.begin(); it!=it_end; ++it ) {
		dumpChildrenOf(it->first, outfile, 2);
	 }

	fflush(outfile);
	fclose(outfile); outfile = NULL;
 }

void ObjectAllocator::dumpChildrenOf( const string& classname, FILE* outfile, int indent ) {
	int i=-1; while(++i<=indent) { fputs(" ", outfile); };
	ObjectTypeNode* objNode = this->typesInfo.find(classname);
	if ( objNode == NULL ) {
		fprintf(outfile, "- NULL!? [%s]\n", classname.c_str() );
		return;
	 }
	if ( objNode->children.empty() )
		fputs("-", outfile);
	else
		fputs("+", outfile);
	fputs(" ", outfile);
	if ( objNode->info == NULL ) {
		fprintf(outfile, "%s [dummy]\n", classname.c_str() );
		return;
	 }
	else {
		fprintf(outfile, "%s [%g] {%s}\n", objNode->info->name.c_str(), objNode->info->version, objNode->info->parent.c_str() );
	 }
	// recursive
	std::vector<std::string>::const_iterator it;
	for( it=objNode->children.begin(); it!=objNode->children.end(); ++it ) {
		dumpChildrenOf( *it, outfile, indent*2 );
	 }
 }

void ObjectAllocator::dumpParentOf( const string& classname, FILE* outfile, int indent ) {
	int i=-1; while(++i<=indent) { fputs(" ", outfile); };
	ObjectTypeNode* objNode = this->typesInfo.find(classname);
	if ( objNode == NULL ) {
		fprintf(outfile, "- NULL!? [%s]\n", classname.c_str() );
		return;
	 }
	if ( objNode->info == NULL ) {
		fprintf(outfile, "%s [dummy]\n", classname.c_str() );
		return;
	 }
	if ( !objNode->info->parent.empty() )
		dumpParentOf(objNode->info->parent, outfile, indent+2 );
 }


/////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
